
此系列文章是以我的業餘專案: Kimoji 作為範例。
這款以純 Jetpack Compose 撰寫的 side project,已經在 Google Play 上架。 歡迎試玩!
立馬下載
延續昨天的文章,今天我們來把 UI 狀態 (也就是日記清單) 搬到 ViewModel,然後開始抽出事務邏輯。
DiaryViewModel.kt,並定義 ViewModel class。把我們的「資料來源」getDiaries() 移到 DiaryViewModel。
定義 _diaries 內部變數,透過前兩天文章介紹的的方法,使用 toMutableStateList 來指定初值,然後曝露 diaries 日記清單,讓它的值只能透過 ViewModel 來變更。
我們使用 List 內建的 remove 函式,來實作簡單的刪除功能。
import androidx.compose.runtime.toMutableStateList
import androidx.lifecycle.ViewModel
class DiaryViewModel : ViewModel() {
    private val _diaries = getDiaries().toMutableStateList()
    val diaries: List<Diary>
        get() = _diaries
   fun remove(diary: Diary) {
       _diaries.remove(diary)
   }
}
private fun getDiaries() = List(30) { i -> Diary(i, "Day # $i") }
viewModel() 函式,從任何 composable 來存取這個 ViewModel。如果要使用這個函式,開啟 app/build.gradle 檔案並加入以下 library,然後在 Android Studio sync dependencies:
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.4.1")
DiaryScreen。新增 diaryViewModel 當做 DiaryScreen composable 的參數,並呼叫  viewModel() 來初始化 ViewModel instance。這樣的介面設計,有助於在測試這個 composable 時抽換 ViewModel,並可在需要時 hoist state holder。接著,我們將「日記清單」和「刪除函式」傳給 Journal composable,並移除我們之前定義的 onDelete lambda 函式。import androidx.lifecycle.viewmodel.compose.viewModel
@Composable
fun DiaryScreen(
    modifier: Modifier = Modifier,
    diaryViewModel: DiaryViewModel = viewModel()
  ) {
   Column(modifier = modifier) {
       StatefulCounter()
        Journal(
          diaries = diaryViewModel.diaries, 
          onDelete = { diary -> diaryViewModel.remove(diary) }
        )
   }
}
viewModel() 會傳回現有的 ViewModel 或在指定 scope 內建立新的 ViewModel。只要 scope 還存續著,系統就會保留 ViewModel 的 instance。例如,如果在 Activity 中使用 composable,viewModel() 會傳回相同的 instance,直到 Activity finish 或 kill process 為止。
我們已經把畫面上一部分狀態和事務邏輯與 ViewModel 整合。由於狀態會保留在 Composition 之外,並由 ViewModel 儲存,因此對「日記清單」的變動就可以撐過 configuration changes 了。
ViewModel 無法在所有情況下自動維持 app 的狀態 (例如系統觸發的 process death)。如果想詳細瞭解如何維持 app 的 UI 狀態,可以參閱官方說明文件。
ViewModel 適合被宣告在畫面層級的 composable,「畫面層級的 composable」代表它比較靠近從
Activity、Fragment或 Navigation graph destination 呼叫的 root composable。ViewModel不應向下傳遞給其他 composable,而應該只往下傳遞需要的資料,以及執行必要邏輯的函式。如果想深入瞭解,請參閱 ViewModel 和 state holder,以及官方的 Compose and other libraries 說明文件。
此系列文章是以我的業餘專案:Kimoji 為範例。
Kimoji 是一款心情日記 App,讓你用可愛的 emoji 來撰寫你的心情日記。現在就來試試這款設計精美的微日記吧!
立馬下載
Reference: https://developer.android.com/codelabs/jetpack-compose-state